/*
* Copyright (c) 2010 The Turing Project
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* Except as contained in this notice, the name(s) of the above
* copyright holders shall not be used in advertising or otherwise to
* promote the sale, use or other dealings in this Software without prior
* written authorization.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
 */

package userinterface;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import javax.swing.JPanel;
import simulator.*;
import java.awt.geom.*; // For Ellipse2D, etc.

/**
 * A panel which displays the Z-plane.
 */
public class ProjectionPanel extends JPanel {
    private ProjectionBox _box = new ProjectionBox();
    private Model _model = new Model();
    private Rectangle2D.Double _zFrame;

    private int _nodeRadius = 2;
    private int _nodeDiameter = _nodeRadius * 2;


    private Color _lightOrange = new Color(230, 220, 0);
    private Color _darkRed = new Color(235, 50, 50);




    public ProjectionPanel() {
      _zFrame = new Rectangle2D.Double(10, 10, 10 + _box.getWidth(), 10 + _box.getWidth());
    }

    /**
     * Called to update the ProjectionBox and Model objects stored in ProjectionPannel
     * and used to draw the projection.
     * @param _box
     * @param _model
     */
    public void setProjection(ProjectionBox box, Model model) {
        _box = box;
        _model = model;
    }



    /**
     * Draws to the ProjectionPanel class, depicting the _model as viewed from the x, y, and z directions.
     * @param g Graphics object.
     * @override
     */
    public void paintComponent(Graphics g) {
          super.paintComponent(g);

          Graphics2D g2d = (Graphics2D) g;

          g2d.setColor(Color.blue);

          plotProjection(0, g2d);
          plotProjection(1, g2d);
          plotProjection(2, g2d);

          _model.redrawTraces(g2d);

          plotData(g2d);

          Dimension size = getSize();
          Insets insets = getInsets();

          int w =  size.width - insets.left - insets.right;
          int h =  size.height - insets.top - insets.bottom;

      }


    /**
     * Displays data related to the simulation in the lower right quadrent
     * of the window.
     * @param g2d
     */
    private void plotData (Graphics2D g2d) {
        int xOffset = 10; // Offset from window edge.
        int yOffset = 10; // Offset from window edge.
        int frameSpacing = 10; // Distance between adjacent frames.
        int frameHeight = _box.getWidth();
        int frameWidth = _box.getWidth();
        int lineSpacing = 20;

        yOffset += (_box.getWidth() *2 + frameSpacing * 3);

        g2d.drawString(String.format("Model Time: %1$.1f s", _model.getTime()), xOffset, yOffset);
        g2d.drawString(String.format("Frame Width: %1$.1e m", _box.getWidth() * _box.getScale()), xOffset,  yOffset + lineSpacing);
    }

    /**
     * Draws a projection of model when viewed along one of three axis: x = 0, y = 1,
     * and z = 2.
     * @param axis
     * @param g2d
     */
      private void plotProjection(int axis, Graphics2D g2d) {
          int xMap = 0;
          int yMap = 1;
          int xOffset = 10; // Offset from window edge.
          int yOffset = 10; // Offset from window edge.
          int frameSpacing = 10; // Distance between adjacent frames.
          int frameHeight = _box.getWidth();
          int frameWidth = _box.getWidth();

          if (axis == 0){
              xMap = 2;
              yMap = 1;
              frameWidth = _box.getHeight();
              xOffset += (_box.getWidth() + frameSpacing);
          } else if (axis == 1){
              xMap = 0;
              yMap = 2;
              frameHeight = _box.getHeight();
              yOffset += (_box.getWidth() + frameSpacing);
          }

          Rectangle2D.Double frame = new Rectangle2D.Double(xOffset, yOffset, frameWidth, frameHeight);
          g2d.draw(frame);

          switch (axis) {
              case 0: g2d.drawString("X Projection Frame", xOffset + 5, yOffset + 15); break;
              case 1: g2d.drawString("Y Projection Frame", xOffset + 5, yOffset + 15); break;
              case 2: g2d.drawString("Z Projection Frame", xOffset + 5, yOffset + 15); break;
          }

          // Adjust offsets to place (0,0,0) in the center of the projection frame.
          xOffset += frameWidth/2;
          yOffset += frameHeight/2;

          // Draw all visible _model nodes.
          for (int i=0; i < _model.countNodes(); i++) {
             int x = (int) ((_model.getNodeCoordinate(i, xMap) - _box.getCenter(xMap)) * _box.getScale());
             int y = (int) ((_model.getNodeCoordinate(i, yMap) - _box.getCenter(yMap)) * _box.getScale());
             int radius = (int) (_model.getNodeRadius(i));
             int diameter = (int) (_model.getNodeDiameter(i));

             if (x < frameWidth && y < frameHeight) {
                 if (axis == 1) {
                     y = -y;
                 } else if (axis == 0) {
                     x = -x;
                 }
                 x += xOffset - radius;
                 y += yOffset - radius;
                 g2d.drawArc(x, y, diameter, diameter, 0, 360);
             }

          }

          // Draw all visible _model edges.
          for (int i=0; i < _model.countEdges(); i++) {
             int x1 = (int) ((_model.getEdgeCoordinate(i, 0, xMap) - _box.getCenter(xMap)) * _box.getScale());
             int y1 = (int) ((_model.getEdgeCoordinate(i, 0, yMap) - _box.getCenter(yMap)) * _box.getScale());
             int x2 = (int) ((_model.getEdgeCoordinate(i, 1, xMap) - _box.getCenter(xMap)) * _box.getScale());
             int y2 = (int) ((_model.getEdgeCoordinate(i, 1, yMap) - _box.getCenter(yMap)) * _box.getScale());
             if (x1 < frameWidth &&
                 x2 < frameWidth &&
                 y1 < frameHeight &&
                 y2 < frameHeight) {
                      if (axis == 1) {
                        y1 = -y1;
                        y2 = -y2;
                      } else if (axis == 0) {
                        x1 = -x1;
                        x2 = -x2;
                      }
                      x1 += xOffset;
                      y1 += yOffset;
                      x2 += xOffset;
                      y2 += yOffset;
                      g2d.drawLine(x1, y1, x2, y2);
             }
          }
      }
    }

